﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Mvc.Ajax;
using System.Web.Routing;
using System.Linq.Dynamic;
using Q42.Wheels.Mvc.Paging.Utils;

namespace Q42.Wheels.Mvc.Paging
{
  public static class PagingExtensions
  {
    public static Paging GetPaging(this ViewDataDictionary viewData)
    {
      return GetPaging(viewData, Paging.DefaultPagingName);
    }

    public static Paging GetPaging(this ViewDataDictionary viewData, string pagingName)
    {
      return viewData[pagingName] as Paging;
    }

    public static IEnumerable<T> GetPage<T>(this IEnumerable<T> enumerable, Paging paging)
    {
      return enumerable.AsQueryable().GetPage(paging);
    }

    public static IQueryable<T> GetPage<T>(this IQueryable<T> query, Paging paging)
    {
      paging.TotalNumber = query.Count();

      if (!String.IsNullOrWhiteSpace(paging.Sort))
      {
        query = query.OrderBy(paging.Sort + (paging.SortDescending ? " desc" : " asc"));
      }

      return query.Skip(paging.CurrentPage * paging.NumberPerPage).Take(paging.NumberPerPage);
    }

    public static IEnumerable<T> GetPage<T>(this ViewPage view, IEnumerable<T> enumerable)
    {
      return enumerable.GetPage(view.ViewData.GetPaging());
    }

    public static IEnumerable<T> GetPage<T>(this WebViewPage view, IEnumerable<T> enumerable)
    {
      return enumerable.GetPage(view.ViewData.GetPaging());
    }

    public static IEnumerable<T> GetPage<T>(this ControllerBase controller, IEnumerable<T> enumerable)
    {
      return enumerable.GetPage(controller.ViewData.GetPaging());
    }

    #region regular

    public static MvcHtmlString RenderPageLink(this HtmlHelper helper, string linkText, int page)
    {
      return RenderPageLink(helper, helper.ViewData.GetPaging(), linkText, page, null);
    }

    public static MvcHtmlString RenderPageLink(this HtmlHelper helper, Paging paging, string linkText, int page)
    {
      return RenderPageLink(helper, paging, linkText, page, null);
    }

    public static MvcHtmlString RenderPageLink(this HtmlHelper helper, string linkText, int page, object htmlAttributes, bool canBeSelected = true)
    {
      return RenderPageLink(helper, helper.ViewData.GetPaging(), linkText, page, htmlAttributes, canBeSelected);
    }

    public static MvcHtmlString RenderPageLink(this HtmlHelper helper, Paging paging, string linkText, int page, object htmlAttributes, bool canBeSelected = true)
    {
      IDictionary<string, object> htmlAttributesDictionary = DictionaryUtils.AnonymousObjectToDictionary(htmlAttributes);
      if (!htmlAttributesDictionary.ContainsKey("class"))
        htmlAttributesDictionary["class"] = "paginationlink";
      
      if (paging.CurrentPage == page && canBeSelected)
      {
        htmlAttributesDictionary["class"] = String.Format("{0} selected {0}_selected", htmlAttributesDictionary["class"]);
      }

      RouteValueDictionary newDict = DictionaryUtils.GetRouteDataForLink(helper.ViewContext, paging, page, null);

      return helper.ActionLink(linkText, null, null, newDict, htmlAttributesDictionary);
    }

    public static MvcHtmlString RenderSortLink(this HtmlHelper helper, string sortField)
    {
      return RenderSortLink(helper, helper.ViewData.GetPaging(), sortField, sortField, null);
    }

    public static MvcHtmlString RenderSortLink(this HtmlHelper helper, Paging paging, string sortField)
    {
      return RenderSortLink(helper, paging, sortField, sortField, null);
    }

    public static MvcHtmlString RenderSortLink(this HtmlHelper helper, string linkText, string sortField)
    {
      return RenderSortLink(helper, helper.ViewData.GetPaging(), linkText, sortField, null);
    }

    public static MvcHtmlString RenderSortLink(this HtmlHelper helper, Paging paging, string linkText, string sortField)
    {
      return RenderSortLink(helper, paging, linkText, sortField, null);
    }

    public static MvcHtmlString RenderSortLink(this HtmlHelper helper, string linkText, string sortField, object htmlAttributes)
    {
      return RenderSortLink(helper, helper.ViewData.GetPaging(), linkText, sortField, htmlAttributes);
    }

    public static MvcHtmlString RenderSortLink(this HtmlHelper helper, Paging paging, string linkText, string sortField, object htmlAttributes)
    {
      IDictionary<string, object> htmlAttributesDictionary = DictionaryUtils.AnonymousObjectToDictionary(htmlAttributes);

      if (!htmlAttributesDictionary.ContainsKey("class"))
        htmlAttributesDictionary["class"] = "sortlink";
      else
        htmlAttributesDictionary["class"] += " sortlink";

      if (sortField == paging.Sort)
      {
        htmlAttributesDictionary["class"] += " selected";
        if(paging.SortDescending)
          htmlAttributesDictionary["class"] += " down";
        else
          htmlAttributesDictionary["class"] += " up";
      }

      RouteValueDictionary newDict = DictionaryUtils.GetRouteDataForLink(helper.ViewContext, paging, null, sortField);

      return helper.ActionLink(linkText, null, null, newDict, htmlAttributesDictionary);
    }

    public static MvcHtmlString RenderPagination(this HtmlHelper helper)
    {
      return RenderPagination(helper, helper.ViewData.GetPaging());
    }

    public static MvcHtmlString RenderPagination(this HtmlHelper helper, Paging paging)
    {
      var partialView = ViewEngines.Engines.FindPartialView(helper.ViewContext.Controller.ControllerContext, paging.PaginationUserControlName);
      if (partialView.View != null)
      {
        return helper.Partial(paging.PaginationUserControlName, paging, helper.ViewData);
      }
      else
      {
        StringBuilder sb = new StringBuilder();
        sb.Append("<div class=\"pagination\">");

        for (int i = 0; i < paging.NumberPages; i++)
        {
          sb.Append(RenderPageLink(helper, paging, (i + 1).ToString(), i));
        }

        sb.Append("</div>");

        return MvcHtmlString.Create(sb.ToString());
      }
    }

    #endregion

    #region AJAX

    public static MvcHtmlString RenderPageLink(this AjaxHelper helper, string linkText, int page, string updateTargetId)
    {
      return RenderPageLink(helper, helper.ViewData.GetPaging(), linkText, page, updateTargetId, null);
    }

    public static MvcHtmlString RenderPageLink(this AjaxHelper helper, string linkText, int page, string updateTargetId, object htmlAttributes, bool canBeSelected = true)
    {
      return RenderPageLink(helper, helper.ViewData.GetPaging(), linkText, page, updateTargetId, htmlAttributes, canBeSelected);
    }

    public static MvcHtmlString RenderPageLink(this AjaxHelper helper, Paging paging, string linkText, int page, string updateTargetId)
    {
      return RenderPageLink(helper, paging, linkText, page, updateTargetId, null);
    }

    public static MvcHtmlString RenderPageLink(this AjaxHelper helper, Paging paging, string linkText, int page, string updateTargetId, object htmlAttributes, bool canBeSelected = true)
    {
      IDictionary<string, object> htmlAttributesDictionary = DictionaryUtils.AnonymousObjectToDictionary(htmlAttributes);

      if (!htmlAttributesDictionary.ContainsKey("class"))
        htmlAttributesDictionary["class"] = "paginationlink";

      if (paging.CurrentPage == page && canBeSelected)
      {
        if (htmlAttributesDictionary.ContainsKey("class"))
        {
          htmlAttributesDictionary["class"] = String.Format("{0} selected {0}_selected", htmlAttributesDictionary["class"]);
        }
        else
        {
          htmlAttributesDictionary["class"] = "selected";
        }
      }

      RouteValueDictionary newDict = DictionaryUtils.GetRouteDataForLink(helper.ViewContext, paging, page, null);

      return helper.ActionLink(linkText, null, null, newDict, new AjaxOptions { HttpMethod = "Get", UpdateTargetId = updateTargetId }, htmlAttributesDictionary);
    }

    public static MvcHtmlString RenderSortLink(this AjaxHelper helper, string sortField, string updateTargetId)
    {
      return RenderSortLink(helper, helper.ViewData.GetPaging(), sortField, sortField, updateTargetId, null);
    }

    public static MvcHtmlString RenderSortLink(this AjaxHelper helper, Paging paging, string sortField, string updateTargetId)
    {
      return RenderSortLink(helper, paging, sortField, sortField, updateTargetId, null);
    }

    public static MvcHtmlString RenderSortLink(this AjaxHelper helper, string linkText, string sortField, string updateTargetId)
    {
      return RenderSortLink(helper, helper.ViewData.GetPaging(), linkText, sortField, updateTargetId, null);
    }

    public static MvcHtmlString RenderSortLink(this AjaxHelper helper, string linkText, string sortField, string updateTargetId, object htmlAttributes)
    {
      return RenderSortLink(helper, helper.ViewData.GetPaging(), linkText, sortField, updateTargetId, htmlAttributes);
    }

    public static MvcHtmlString RenderSortLink(this AjaxHelper helper, Paging paging, string linkText, string sortField, string updateTargetId, object htmlAttributes)
    {
      IDictionary<string, object> htmlAttributesDictionary = DictionaryUtils.AnonymousObjectToDictionary(htmlAttributes);

      if (!htmlAttributesDictionary.ContainsKey("class"))
        htmlAttributesDictionary["class"] = "sortlink";

      if (sortField == paging.Sort)
      {
        htmlAttributesDictionary["class"] += " selected";
        if (paging.SortDescending)
          htmlAttributesDictionary["class"] += " down";
        else
          htmlAttributesDictionary["class"] += " up";
      }

      RouteValueDictionary newDict = DictionaryUtils.GetRouteDataForLink(helper.ViewContext, paging, null, sortField);

      return helper.ActionLink(linkText, null, null, newDict, new AjaxOptions { HttpMethod = "Get", UpdateTargetId = updateTargetId }, htmlAttributesDictionary);
    }

    public static MvcHtmlString RenderPagination(this AjaxHelper helper, string updateTarget)
    {
      return RenderPagination(helper, helper.ViewData.GetPaging(), updateTarget);
    }

    public static MvcHtmlString RenderPagination(this AjaxHelper helper, Paging paging, string updateTarget)
    {
      StringBuilder sb = new StringBuilder();
      sb.Append("<div class=\"pagination\">");

      for (int i = 0; i < paging.NumberPages; i++)
      {
        sb.Append(RenderPageLink(helper, (i + 1).ToString(), i, updateTarget));
      }

      sb.Append("</div>");

      return MvcHtmlString.Create(sb.ToString());
    }

    #endregion
  }
}
